package lib

import (
	"fmt"
	"gogame/logger"
	"reflect"
	"runtime/debug"
	"strings"
)

const (
	WCCHAR      = "*"
	DELLIMITER  = "."
	CALLBACKKEY = "__callbacks__"
)

type method struct {
	on       int
	args     []reflect.Type
	function reflect.Value
}

type branch struct {
	name   string
	parent string

	callbacks []*method

	child map[string]*branch
}

type EventEmitter struct {
	tree map[string]*branch
}

var GlobalEvent = NewEventEmitter()

func NewEventEmitter() *EventEmitter {
	return &EventEmitter{
		tree: map[string]*branch{
			CALLBACKKEY: {
				name:   CALLBACKKEY,
				parent: CALLBACKKEY,
				child:  map[string]*branch{},
			},
		},
	}
}

func (e *EventEmitter) On(topic string, fn interface{}, once ...int) {
	f, ok := fn.(reflect.Value)
	if !ok {
		f = reflect.ValueOf(fn)
	}
	if f.Kind() != reflect.Func {
		panic(fmt.Errorf("function must be procfunc or bound method"))
	}

	t := f.Type()
	m := method{
		function: f,
		args:     []reflect.Type{},
	}
	if len(once) == 1 {
		m.on = once[0]
	}
	for i := 0; i < t.NumIn(); i++ {
		m.args = append(m.args, t.In(i))
	}

	_branch_ := e.tree[CALLBACKKEY]
	parts := strings.Split(topic, DELLIMITER)
	for _, p := range parts {
		if c, ok := _branch_.child[p]; ok {
			_branch_ = c
		} else {
			_branch_.child[p] = &branch{
				name:      p,
				parent:    _branch_.name,
				callbacks: []*method{},

				child: map[string]*branch{},
			}

			_branch_ = _branch_.child[p]
		}
	}

	_branch_.callbacks = append(_branch_.callbacks, &m)
}

func (e *EventEmitter) Emit(topic string, args ...interface{}) {
	_branch_ := []*branch{e.tree[CALLBACKKEY]}
	parts := strings.Split(topic, DELLIMITER)
	for _, p := range parts {
		var _branches_ []*branch
		for _, b := range _branch_ {
			for k, t := range b.child {
				if k == p {
					_branches_ = append(_branches_, t)
				} else if p == WCCHAR || k == WCCHAR {
					_branches_ = append(_branches_, t)
				}
			}
		}

		_branch_ = _branches_
	}

	for _, b := range _branch_ {
		for i, m := range b.callbacks[:] {
			var argv []reflect.Value
			for _, arg := range args {
				argv = append(argv, reflect.ValueOf(arg))
			}

			go func(doFunc *method) {
				defer func() {
					if err := recover(); err != nil {
						logger.WorkerLog.Warn(fmt.Sprintf("emitter error-->%s", err))
						debug.PrintStack()
					}
				}()
				doFunc.function.Call(argv)
			}(m)

			if m.on != 0 {
				if m.on-1 > 0 {
					m.on--
				} else {
					b.callbacks = append(b.callbacks[0:i], b.callbacks[i:]...)
				}
			}
		}
	}
}

func (e *EventEmitter) Off(topic string, fn interface{}) {
	ft := reflect.ValueOf(fn)

	_branch_ := []*branch{e.tree[CALLBACKKEY]}
	parts := strings.Split(topic, DELLIMITER)
	for _, p := range parts {
		var _branches_ []*branch
		for _, b := range _branch_ {
			for k, t := range b.child {
				if k == p {
					_branches_ = append(_branches_, t)
				} else if p == WCCHAR || k == WCCHAR {
					_branches_ = append(_branches_, t)
				}
			}
		}

		_branch_ = _branches_
	}

	for _, b := range _branch_ {
		if ft == reflect.ValueOf(nil) {
			b.callbacks = []*method{}
		} else {
			for i, m := range b.callbacks[:] {
				if m.function == ft {
					b.callbacks = append(b.callbacks[0:i], b.callbacks[i:]...)
				}
			}
		}
	}
}
